//////////////////////////////////////////////////////////////////////////////

//  Container: cycle array

//////////////////////////////////////////////////////////////////////////////

#ifndef	__MAGUS_CYCLE_ARRAY_HPP
#define	__MAGUS_CYCLE_ARRAY_HPP

//////////////////////////////////////////////////////////////////////////////

#include <iterator>
#include <cstddef>		// for "ptrdiff_t"
#include <boost/static_assert.hpp>
#include <cassert>

//////////////////////////////////////////////////////////////////////////////

namespace magus
{

	//// Cycle iterator //////////////////////////////////////////////////////////

	template <typename T, int N>
	class cycle_iterator
	{
	public:
		BOOST_STATIC_ASSERT(N>0);

		typedef T									value_type;
		typedef ptrdiff_t							distance_type;
		typedef std::random_access_iterator_tag		iterator_category;
		typedef T*									pointer;
		typedef const T*							const_pointer;
		typedef T&									reference;
		typedef const T&							const_reference;

		// Constructors
		explicit cycle_iterator(T* pdata, int initial_pos)
			: _M_pdata(pdata), _M_offset(initial_pos)
		{}

		cycle_iterator(const cycle_iterator& rhs)
			: _M_pdata(rhs._M_pdata), _M_offset(rhs._M_offset)
		{}

		// Operators
		cycle_iterator& operator=(const cycle_iterator& rhs)
		{
			// check for self assginment
			if( this == &rhs )
				return *this;

			_M_pdata = rhs._M_pdata;
			_M_offset = rhs._M_offset;

			return *this;
		}

		reference operator*() { return *(_M_pdata + _M_offset%N); }
		reference operator*() const { return *(_M_pdata + _M_offset%N); }

		pointer operator->() { return _M_pdata + _M_offset%N; }
		pointer operator->() const { return _M_pdata + _M_offset%N; }

		cycle_iterator& operator++()
		{
			_M_offset++;
			return *this;
		}

		const cycle_iterator operator++(int)
		{
			cycle_iterator tmp(*this);
			++(*this);
			return tmp;
		}

		cycle_iterator& operator--()
		{
			_M_offset--;
			return *this;
		}

		const cycle_iterator operator--(int)
		{
			cycle_iterator tmp(*this);
			--(*this);
			return tmp;
		}

		const cycle_iterator operator+(int rhs) const
		{
			cycle_iterator tmp(*this);
			tmp += rhs;	
			return tmp;
		}

		cycle_iterator& operator+=(int rhs)
		{
			_M_offset += rhs;
			return *this;
		}

		distance_type operator-(const cycle_iterator& rhs) const
		{
			return _M_offset - rhs._M_offset;
		}

		const cycle_iterator operator-(int rhs) const
		{
			cycle_iterator tmp(*this);
			tmp -= rhs;
			return tmp;
		}

		cycle_iterator& operator-=(int rhs)
		{
			_M_offset -= rhs;
			return *this;
		}

		bool operator==(const cycle_iterator& rhs) const
		{
			assert( _M_pdata == rhs._M_pdata );
			return _M_offset == rhs._M_offset;
		}

		bool operator!=(const cycle_iterator& rhs) const
		{
			assert( _M_pdata == rhs._M_pdata );
			return _M_offset != rhs._M_offset;
		}

		bool operator>(const cycle_iterator& rhs) const
		{
			assert( _M_pdata == rhs._M_pdata );
			return _M_offset > rhs._M_offset;
		}

		bool operator<(const cycle_iterator& rhs) const
		{
			assert( _M_pdata == rhs._M_pdata );
			return _M_offset < rhs._M_offset;
		}

		bool operator>=(const cycle_iterator& rhs) const
		{
			assert( _M_pdata == rhs._M_pdata );
			return _M_offset >= rhs._M_offset;
		}

		bool operator<=(const cycle_iterator& rhs) const
		{
			assert( _M_pdata == rhs._M_pdata );
			return _M_offset <= rhs._M_offset;
		}

	private:
		T*		_M_pdata;		// Cycle iterate range: [_M_pdata, _M_pdata+N)
		int		_M_offset;		// Logical offset from "begin", may larger than
								// N but not allowed less than 0
	};

	template <typename T, int N>
	inline cycle_iterator<T, N> operator+(int lhs, const cycle_iterator<T, N>& rhs)
	{
		cycle_iterator<T, N> tmp(rhs);
		tmp += rhs;
		return tmp;
	}

	//// Cycle array, STL-like interface /////////////////////////////////////////

	template <typename T, int N>
	class cycle_array
	{
	public:
		typedef T									value_type;
		typedef T*									pointer;
		typedef const T*							const_pointer;
		typedef T&									reference;
		typedef const T&							const_reference;
		typedef cycle_iterator<T, N>				iterator;
		typedef const cycle_iterator<T, N>			const_iterator;
		typedef ptrdiff_t							difference_type;
		typedef size_t								size_type;

		// Constructor & destructor
		cycle_array()
		{
			BOOST_STATIC_ASSERT(N>0);
		}
		~cycle_array() {}

		// Copy constructor
		cycle_array(const cycle_array& rhs)
		{
			BOOST_STATIC_ASSERT(N>0);

			for(int s=0; s<N; s++)
				_M_data[s] = rhs._M_pdata[s];
		}

		// Operator =
		cycle_array& operator=(const cycle_array& rhs)
		{
			// check for self assignment
			if( this == &rhs )
				return *this;

			for(int s=0; s<N; s++)
				_M_data[s] = rhs._M_pdata[s];

			return *this;
		}

		iterator begin() { return iterator(_M_data, 0); }
		const_iterator begin() const { return const_iterator(_M_data, 0); }
		iterator end() { return iterator(_M_data, N); };
		const_iterator end() const { return const_iterator(_M_data, N); }

		reference operator[](int n) { assert(n>=0 && n<N); return _M_data[n]; }
		const_reference operator[](int n) const { assert(n>=0 && n<N); return _M_data[n]; }

		size_type size() const { return N; }

	private:
		T _M_data[N];
	};

}	// namespace magus

//////////////////////////////////////////////////////////////////////////////

#endif		// #ifndef	__MAGUS_CYCLE_ARRAY_HPP
